package cn.biplam.back.operator.tree;

import java.util.ArrayList;

import cn.biplam.common.client.DataFilter;
import cn.biplam.common.client.DbResource;
import cn.biplam.common.client.Operator;
import cn.biplam.common.opcode.SysManageCode.TreeIndexCode;
import cn.biplam.common.paramters.MdParam.Index_Node_Move_Type;
import cn.biplam.common.tools.TOOLS;
import cn.biplam.common.wrapper.ErrorMessage;
import cn.biplam.common.wrapper.sqlresult.SqlResult;
import cn.biplam.common.wrapper.tree.TreeIndexFields;

/**
 *  索引树数据操作—节点移动
 * @author XiaoRuxing Nov 9, 20094:48:18 PM
 * @version 1.0
 */
public class OptIndexTreeMove extends IndexTree{

	protected OptIndexTreeMove(String userId, String tableName,
			TreeIndexFields fields,
			DbResource dbResource) {
		super(userId, tableName, fields, dbResource);
	}

	/**
	 * 根据索引节点ID移动
	 * @param sql
	 * @param filter 传入被移动节点的物理表ID和移动到的节点物理表ID<br>
	 * 				 <ul>需要参数： 
	 * 					  <li>moved_node_id 被移动节点ID 	
	 *                <li>move_to_node_id 移动到节点ID
	 *                <li>move_type 移动类型
	 *           </ul>
	 * @throws ErrorMessage 未指定移动类型抛出异常
	 */
	protected void moveNodeByIndexId(ArrayList<String> sql,DataFilter filter) throws ErrorMessage{
		String srcNode=filter.getValue("moved_node_id");
		String targNode=filter.getValue("move_to_node_id");
		String moveType=filter.getValue("move_type");
		if(srcNode.equals(targNode)){
			return;
		}
		SqlResult srcInfo=null;
		SqlResult targInfo=null;

		/** 获取被移动节点ID */
		filter.put(this.fields.getNodeId(), srcNode);
		srcInfo=Operator.get(TreeIndexCode.Select_Node, userId, filter, dbResource).getSelectResult();

		/** 获取移动到节点ID */
		filter.put(this.fields.getNodeId(), targNode);
		targInfo=Operator.get(TreeIndexCode.Select_Node, userId, filter, dbResource).getSelectResult();

		this._moveNode(srcInfo, targInfo, moveType, sql, filter);
	}

	/**
	 * 根据索引节点对应真实ID移动<font color='red'><B>不建议使用,目前在模型管理索引表中使用</B></font>
	 * @param filter 传入被移动节点的物理表ID和移动到的节点物理表ID<br>
	 * 				 <ul>需要参数： 
	 * 					  <li>moved_node_id 被移动节点ID 	
	 *                    <li>move_to_node_id 移动到节点ID
	 *                    <li>move_type 移动类型
	 *           </ul>
	 * @throws ErrorMessage 未指定移动类型抛出异常
	 */
	protected void moveNode(ArrayList<String> sql,DataFilter filter) throws ErrorMessage{
		String srcNode=filter.getValue("moved_node_id");
		String targNode=filter.getValue("move_to_node_id");
		String moveType=filter.getValue("move_type");
		if(srcNode.equals(targNode)){
			return;
		}
		SqlResult srcInfo=null;
		SqlResult targInfo=null;

		/** 获取被移动节点ID */
		filter.put(this.fields.getRealId(), srcNode);
		srcInfo=Operator.get(TreeIndexCode.Select_By_Rel_ID, userId, filter, dbResource).getSelectResult();

		/** 获取移动到节点ID */
		filter.put(this.fields.getRealId(), targNode);
		targInfo=Operator.get(TreeIndexCode.Select_By_Rel_ID, userId, filter, dbResource).getSelectResult();
		
		this._moveNode(srcInfo, targInfo, moveType, sql, filter);
	}
	
	/**
	 *  移动节点
	 */
	private void _moveNode(SqlResult srcInfo,SqlResult targInfo,String moveType,ArrayList<String> sql,DataFilter filter) throws ErrorMessage{
		if(srcInfo==null||srcInfo.isNull()){
			throw new ErrorMessage("Src table not exist when move tree node in OptTreeIndex!");
		}
		if(targInfo==null||targInfo.isNull()){
			throw new ErrorMessage("Target table not exist when move tree node in OptTreeIndex!");
		}
		/** 被移动节点信息 */
		String srcNodeID=srcInfo.get(0, this.fields.getNodeId());
		String srcParentID=srcInfo.get(0, this.fields.getNodeParent());
		String srcSequence=srcInfo.get(0, this.fields.getNodeSequence());
		String srcOperatorType = srcInfo.get(0, this.fields.getOperatorType());

		/** 移动到节点信息 */
		String targNodeID=targInfo.get(0, this.fields.getNodeId());
		String targParentID=targInfo.get(0, this.fields.getNodeParent());
		String targSequence=targInfo.get(0, this.fields.getNodeSequence());
		String targOperatorType = targInfo.get(0, this.fields.getOperatorType());

		if(moveType.equals(Index_Node_Move_Type.Move_After.toString())){
			_moveAsLBrother(sql, filter, srcNodeID, targNodeID, targParentID, 
					srcParentID, srcSequence, targSequence, 
					srcOperatorType,targOperatorType);
		}else if(moveType.equals(Index_Node_Move_Type.Move_Before.toString())){
			_moveAsBrother(sql, filter, srcNodeID, targNodeID,   targParentID, srcParentID, 
					srcSequence,  targSequence,  
					srcOperatorType,targOperatorType);
		}else if(moveType.equals(Index_Node_Move_Type.Move_Child_End.toString())){
			_moveAsChildEnd(sql, filter, srcNodeID, targNodeID, 
					srcParentID, srcSequence, srcOperatorType);
		}else if(moveType.equals(Index_Node_Move_Type.Move_Child_Head.toString())){
			_moveAsChildHead(sql, filter, srcNodeID, targNodeID, 
					srcParentID, srcSequence, srcOperatorType);
		}else{
			throw new ErrorMessage(" Move type do not have be appointed!");
		}
	}

	/**
	 * 更新被移动节点
	 */
	private void _updateMoveNode(ArrayList<String> sql,String srcNodeID, 
			String srcParentID,String srcSequence) throws ErrorMessage{
		/** 更新被移动节点 */
		DataFilter srcFilter=new DataFilter();
		srcFilter.put(TABLE_NAME, this.tableName);
		srcFilter.put(this.fields.getNodeId(), srcNodeID);
		srcFilter.put(this.fields.getNodeParent(), srcParentID);
		srcFilter.put(this.fields.getNodeSequence(), srcSequence);
		this.update(sql, srcFilter);
	}

	/**
	 * <ul>移动节点到目标节点的最小儿子(如果被移动节点是目标节点的最小儿子不移动)
	 *  <li> 被移动节点的弟弟向前移动一位
	 *  <li> 被移动节点的新位置为目标节点儿子末尾<BR>
	 *  <li> 如果是主题节点将会被移动到叶子节点前
	 *</ul> 
	 */
	private void _moveAsChildEnd(ArrayList<String> sql,DataFilter filter,
			String srcNodeID,String targNodeID,
			String srcParentID,String srcSequence, 
			String srcOperatorType) throws ErrorMessage{
		//获取目标节点的最小儿子
		String litestChild = this.littleChild(targNodeID, null);
		if(srcNodeID.equals(litestChild)){//如果被移动节点是目标节点的最小儿子不移动
			return;
		}
		// 获取节点的新位置排序
		int orderNumb = this.orderNumByOperatorType(targNodeID, null, srcOperatorType);
		srcSequence = TOOLS.intToString(orderNumb);
		//设置被移动节点所有弟弟向前移动一位
		this.forwardSiblingSequence(sql, srcParentID, srcSequence, srcOperatorType);
		//设置被移动节点的父节点为移动到节点
		srcParentID=targNodeID;
		//更新节点
		_updateMoveNode(sql, srcNodeID,  srcParentID, srcSequence);
	}

	/**
	 * <ul>移动节点为目标节点的大儿子(如果被移动节是目标节点的大儿子不移动)
	 * <li> 被移动节点的弟弟向前移动一位
	 * <li> 目标节点的儿子向后移动一位
	 * <li> 被移动节点的位置为目标节点的儿子头
	 *</ul> 
	 */
	private void _moveAsChildHead(ArrayList<String> sql,DataFilter filter,String srcNodeID,String targNodeID,
			String srcParentID,String srcSequence, 
			String srcOperatorType) throws ErrorMessage{
		//获取目标节点的最大儿子
		String greatestChild= this.greatestChild(targNodeID, null);
		if(srcNodeID.equals(greatestChild)){//如果被移动节是目标节点的大儿子不移动
			return;
		}
		// 设置被移动节点的新位置为兄弟中排第一 
		int order = this.getOrder(srcOperatorType, 1);
		srcSequence=TOOLS.intToString(order);
		//设置移动到节点的所有与被移动节点类型相同的儿子向后移动一位
		this.afterSiblingSequence(sql, targNodeID, 
				"0",  //从排序0开始的所有儿子
				srcOperatorType);
		//被移动节点的弟弟向前移动一位
		this.forwardSiblingSequence(sql, srcParentID, srcSequence, srcOperatorType);
		
		srcParentID=targNodeID;//设置被移动节点的父节点为移动到节点
		//更新被移动节点
		_updateMoveNode(sql, srcNodeID, srcParentID, srcSequence);
	}

	/**
	 * (移动的源和目标必须是同种类型的节点)<BR>
	 * 
	 * <ul> 移动节点为目标节点的哥哥(如果移动节点是目标节点的哥哥则不移动)<BR>
	 * 	<li> 被移动节点的弟弟向前移动一位
	 *  <li> 目标节点及其弟弟向后移动一位
	 *  <li> 被移动节点的位置为目标节点位置
	 *</ul>  
	 */
	private void _moveAsBrother(ArrayList<String> sql,DataFilter filter,
			String srcNodeID,String targNodeID, 
			String targParentID,
			String srcParentID,
			String srcSequence, 
			String targSequence,
			String srcOperatorType,
			String targOperatorType) throws ErrorMessage{
		//如果移动节点是目标节点的哥哥则不移动
		if(srcParentID.equals(targParentID)&&
				TOOLS.stringToInt(srcSequence)+1 == TOOLS.stringToInt(targSequence)
				){
			return;
		}
		//被移动节点的弟弟向前移动一位
		this.forwardSiblingSequence(sql, srcParentID, srcSequence, srcOperatorType);
		
		//如果被移动节点和移动到节点属于同一父亲并且在移动到节点前
		if(srcParentID.equals(targParentID)&&
				TOOLS.stringToInt(srcSequence)<TOOLS.stringToInt(targSequence)){
			srcSequence = (TOOLS.stringToInt(targSequence)-1)+"";// 设置被移动节点的sequence为移动到节点的sequence-1
			this.afterSiblingSequence(sql, targParentID, 
					(TOOLS.stringToInt(targSequence)-2),//目标节点在被移动节点后面已经被向前移动了一位
					targOperatorType);//设置移动到节点及其所有弟弟的向后移动一位
		}else{
			this.afterSiblingSequence(sql, 
					targParentID, 
					TOOLS.stringToInt(targSequence)-1,
					targOperatorType);//设置移动到节点及其所有弟弟的向后移动一位
			srcSequence=targSequence;//设置被移动节点的sequence为移动到节点的sequence
		}
		srcParentID = targParentID;//设置被移动节点的父节点为移动到节点的父亲
		//更新被移动节点
		_updateMoveNode(sql, srcNodeID, srcParentID, srcSequence);
	}

	/**
	 * (移动的源和目标必须是同种类型的节点)<BR>
	 * 
	 * <ul> 移动节点为目标节点的弟弟(如果移动节点是目标节点的弟弟则不移动)
	 *  <li> 被移动节点的弟弟向前移动一位
	 *  <li> 目标节点的弟弟向后移动一位
	 *  <li> 被移动节点的位置为目标节点位置后面
	 *</ul> 
	 */
	private void _moveAsLBrother(ArrayList<String> sql,DataFilter filter,String srcNodeID,String targNodeID, 
			String targParentID,
			String srcParentID,String srcSequence ,String targSequence, 
			String srcOperatorType,
			String targOperatorType) throws ErrorMessage{
		//如果移动节点是目标节点的弟弟则不移动
		if(srcParentID.equals(targParentID)&& 
				(TOOLS.stringToInt(srcSequence) == 
					TOOLS.stringToInt(targSequence)+1)){
			return;
		}
		//被移动节点的弟弟向前移动一位
		this.forwardSiblingSequence(sql, srcParentID, srcSequence, srcOperatorType);
		//如果被移动节点和移动到节点属于同一父亲并且在移动到节点前
		if(srcParentID.equals(targParentID)&&
				TOOLS.stringToInt(srcSequence)<TOOLS.stringToInt(targSequence)){
			srcSequence=targSequence;
			this.afterSiblingSequence(sql, targParentID, (TOOLS.stringToInt(targSequence)-1)+"",
					targOperatorType);//设置移动到节点所有弟弟向后移动一位
		}else{
			this.afterSiblingSequence(sql, targParentID, targSequence,targOperatorType);//设置移动到节点的所有弟弟向后移动一位
			srcSequence = (TOOLS.stringToInt(targSequence)+1)+""; //设置被移动节点的顺序为移动到节点的顺序+1
		}
		srcParentID = targParentID;//设置被移动节点的父节点为移动到节点的父亲
		//更新被移动节点
		_updateMoveNode(sql, srcNodeID, srcParentID, srcSequence );
	}
}
